<?php
declare (strict_types = 1);

namespace app\controller\admin;


use app\model\Menu;
use app\model\Role;
use think\facade\Db;
use think\facade\Validate;
use think\response\Json;

class AdminMenu extends AdminBase
{

    protected $middleware = [
        'app\middleware\AdminLogin' => ['except' => ['addApi'] ],
        'app\middleware\AdminAuth' => ['except' => ['index','list','addApi'] ]
    ];

    private string $menuName = '菜单管理';


    /**
     * 显示资源列表
     * @throws
     * @return Json
     */
    public function index():Json
    {
        $cache_name = 'user_system_menu_'.$this->adminId;
        if(cache('?'.$cache_name)){
            $result = cache($cache_name);
        }else{
            $map = [
                ['type','=',1]
            ];
            if($this->adminInfo['status'] < 90){
                $menu_ids = $this->getPower($this->adminInfo['role_id']);
                $map[] = ['id','in',$menu_ids];
            }

            $data = Menu::where('parent_id',0)
                ->where($map)
                ->order('sort','asc')
                ->select()->toArray();

            $this->nextList($data,'menu',$map);

            $result = $this->setMenu($data);
            //手动添加"个人中心"路由
            $result[] = [
                'path'=> '/personal',
                'name'=> 'personal',
                'component'=> 'personal/index',
                'meta'=> [
                    'title' => '个人中心',
                    'isLink' => '',
                    'isHide' => true,//是否显示
                    'isKeepAlive' => true,//是否缓存
                    'isAffix' => false,//是否固定
                    'isIframe' => false,//是否内置框架
                    'roles' => [],
                    'icon' => 'iconfont icon-gerenzhongxin',
                ]
            ];
            cache($cache_name,$result,300);//缓存5分钟
        }

        $this->result['data'] = $result;

        return returnAdmin($this->request,$this->result,$this->menuName.'-列表',$this->adminInfo);
    }

    /**
     * 显示资源列表
     * @throws
     * @return Json
     */
    public function list():Json
    {

        $data = Menu::where('parent_id',0)
            ->where('type',1)
            ->order('sort','asc')
            ->select()->toArray();

        $this->nextList($data,'menu',[['type','=',1]]);

        $this->result['data'] = $data;

        return returnAdmin($this->request,$this->result,$this->menuName.'-列表',$this->adminInfo);
    }



    /**
     * 保存编辑的资源
     * @throws
     * @return Json
     */
    public function save():Json
    {
        //获取form表单
        $param = input('param.',[]);

        $param = toUnderline($param);

        $rule = [
            'title' => 'require|max:64',
            'path' => 'require|max:255',
            'name' => 'require|max:64',
        ];
        $message = [
            'title.require' => '菜单名称必填',
            'title.max' => '菜单名称最多64个字符',
            'path.require' => '路由路径必填',
            'path.max' => '路由路径最多255个字符',
            'name.require' => '路由名称必填',
            'name.max' => '路由名称最多64个字符'
        ];
        $validate = Validate::rule($rule)->message($message);
        try{
            if(!$validate->check($param)){
                throw new \Exception($validate->getError());
            }

            $model = new Menu();
            $fields = $model->getFields();
            $pk = $model->getPk();
            $id = $param[$pk] ?? 0;
            if(!$id){
                //新增
                $controllerName = $param['controller']??'';
                $controllerName = $controllerName ? ucfirst($controllerName):'';
                $param['power'] = $controllerName?'guide':'';

                if($param['parent_id']){
                    $pm = $model->find($param['parent_id']);
                    if($pm){
                        $param['paths'] = $pm['paths'] ? $pm['paths'].','.$pm['id'] : $pm['id'];
                    }else{
                        $param['parent_id'] = 0;
                        $param['paths'] = '';
                    }
                }

                $info = $model->create($param,$fields);
                $id = $info->id;
                //生成模型和控制器
                $modelName = $param['model']??'';
                $modelName = $modelName ? ucfirst($modelName):'';
                if($modelName){
                    $modelNameSpace = $this->createClass($modelName,1);
                    if($modelNameSpace && $controllerName){

                        $modelField = Db::name(strToUnderline($modelName))->getFields();
                        $modelFields = [];
                        foreach ($modelField as $k=>$f){
                            $comment =  json_decode($f['comment'],true);
                            if(!$comment)continue;

                            $modelFields[$k] = $comment;
                        }
                        $dataTypes = ['string'=>'s','array'=>'a','int'=>'d','float'=>'f','bool'=>'b'];
                        $searchString = '';//搜索
                        $ruleString = '';//验证规则
                        $messageString = '';//验证报错
                        $showString = '';//表格显示
                        $editString = '';//编辑字段
                        $uploadString = '';//上传数据初始
                        $uploadMethodString = '';//上传方法
                        $ruleS = [
                            'require'     => '必填',
                            'number'      => '必须是数字',
                            'integer'     => '必须是整数',
                            'float'       => '必须是浮点数',
                            'boolean'     => '必须是布尔值',
                            'email'       => '为无效的邮箱地址',
                            'mobile'      => '是无效的手机号',
                            'array'       => '必须是数组',
                            'date'        => '不是有效的日期格式',
                            'file'        => '不是有效的文件',
                            'image'       => '不是有效的图片',
                            'alpha'       => '必须是纯字母',
                            'alphaNum'    => '只能是字母、数字',
                            'alphaDash'   => '只能是字母、数字、_、-',
                            'activeUrl'   => '不是有效的域名或IP',
                            'chs'         => '只能是中文',
                            'chsAlpha'    => '只能是中文、字母',
                            'chsAlphaNum' => '只能是中文、字母、数字',
                            'chsDash'     => '只能是中文、字母、数字、_、-',
                            'url'         => '不是有效的URL地址',
                            'ip'          => '不是有效的IP地址',
                            'dateFormat'  => '日期格式错误%s',
                            'in'          => '不在有效范围%s',
                            'notIn'       => '在禁止范围%s',
                            'between'     => '不在有效范围%s之间',
                            'notBetween'  => '在禁止范围%s之间',
                            'length'      => '长度必须%s',
                            'max'         => '超过最大字符数%s',
                            'min'         => '低于最小字符数%s',
                            'after'       => '日期超出范围',
                            'before'      => '日期超出范围',
                            'expire'      => '不在有效%s日期之内',
                            'allowIp'     => 'IP超出范围',
                            'denyIp'      => 'IP禁止访问',
                            'confirm'     => '与%s输入不一致',
                            'different'   => '与%s输入相同',
                            'egt'         => '必须大于等于%s',
                            'gt'          => '必须大于%s',
                            'elt'         => '必须小于等于%s',
                            'lt'          => '必须小于%s',
                            'eq'          => '必须等于%s',
                            'unique'      => '值已存在',
                            'regex'       => '格式错误',
                            'method'      => 'invalid Request method',
                            'token'       => '无效',
                            'fileSize'    => '文件大小超出',
                            'fileExt'     => '文件后缀不允许',
                            'fileMime'    => '文件类型不允许'
                        ];
                        $uploadIndex = 0;
                        $uploadListString = '';
                        $formDataString = '';
                        $initFileListString = '';
                        foreach ($modelFields as $ky=>$ff){
                            $dT = strtolower($ff['dataType'] ?? 'string');
                            $label = $ff['label'] ?? $ky;
                            //处理搜索
                            if($ff['search']){
                                $prop = strToCamelCase('tp_'.$ky);
                                $exp = $ff['searchExp']??'eq';
                                $sT = $ff['searchType']??'input';
                                $searchTypeOf = $dataTypes[$dT] ?? 's';
                                $searchString .= "          [
                'label'=>'{$label}',//标签
                'prop'=> '{$prop}',//表单名称
                'field' => '{$ky}',//查询字段
                'placeholder'=> '',
                'required'=> false,//是否必填
                'join' => false,//是否关联其他表，关联表需要单独处理
                'expression' => '{$exp}',//查询表达式：like,not like,in,not in,between,not between,null,not null,time,>,<,>=,<=,=,<>(!=),find(FIND_IN_SET)
                'type'=> '{$sT}',//类型：input,select,cascader,date,datetime,year,month,week,datetimerange,daterange
                'typeof' => '{$searchTypeOf}',//强制转换类型：d整数，a数组，s字符串，b布尔类型，f浮点数
                'options' => []
            ],".PHP_EOL;
                            }
                            //处理验证
                            if($ff['rule']){
                                $ruleString .= "                '{$ky}' => '{$ff['rule']}',".PHP_EOL;
                                $ruleArr = explode('|',$ff['rule']);
                                foreach ($ruleArr as $r){
                                    $rus = explode(':',$r);
                                    $ru = $rus[0] ?? '';
                                    $rru = $rus[1] ?? '';
                                    $msg = sprintf($ruleS[$ru]??'',$rru);
                                    $messageString .= "                '$ky.$ru' => '{$label}{$msg}',".PHP_EOL;
                                }
                            }
                            //处理表格展示字段
                            $ky = strToCamelCase($ky);
                            if($ff['show']){
                                $width = $ff['showWidth'] ?? '';
                                $showType = $ff['showType'] ?? 'text';
                                $show_type = $showType === 'image' ? 'image' : 'text';
                                $showString .= "      { key: '{$ky}', colWidth: '{$width}', title: '{$label}', type: '{$show_type}',showType:'{$showType}', isCheck: true },".PHP_EOL;
                            }
                            //处理编辑
                            if($ff['edit']){
                                $editType = strtolower($ff['editType'] ?? 'text');
                                //上传处理
                                $handle = '';
                                $remove = '';
                                $listName = '';
                                if(in_array($editType,['image','video','file'])){
                                    $handle = 'handleUploadSuccess'.$uploadIndex;
                                    $remove = 'handleUploadRemove'.$uploadIndex;
                                    $listName = 'uploadList'.$uploadIndex;
                                    $uploadIndex++;
                                    $initFileListString .= "  state.$listName = [];".PHP_EOL;
                                    $uploadListString .= "  $listName: [],".PHP_EOL;
                                    $uploadMethodString .= "//上传成功
const {$handle} = (
    response:any,
    uploadFile:any
) => {
  if(!response.code){
    ".($dT=='array'?"state.dataForm.{$ky}.push(response.data)":"state.dataForm.{$ky} = response.data").";
    state.{$listName} = [
      {name:uploadFile.name,url:response.data}
    ];
  }else{
    ElMessage.error(response.msg);
  }

}
//清除值
const {$remove} = (uploadFile:any,uploadFiles:any) => {
    let urls = [];
    uploadFiles.forEach(value => {
     urls.push(value.url);
    });
    state.dataForm.{$ky} = ".($dT=='array'?"urls":"urls.join()").";
    state.{$listName} = [];
}".PHP_EOL;

                                    $uploadString .= "
  if(info.{$ky}){
      if(Array.isArray(info.{$ky})){
          if(info.{$ky}.length){
              info.{$ky}.forEach(value => {
                 state.{$listName}.push({name: value.substring(value.lastIndexOf('/')+1), url: value});
              });
          }
      }else{
          state.{$listName} = [
            {name: info.{$ky}.substring(info.{$ky}.lastIndexOf('/')+1), url: info.{$ky}}
          ];
      }
    
  }".PHP_EOL;
                                }
                                $editSize = $ff['editSize'] ?? 24;
                                if($editType == 'radio'){
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">
          <!-- 单选 -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" >
              <el-radio-group v-model="state.dataForm.'.$ky.'" label-width="auto">
                <el-radio v-for="(rItem, ri) in state.radioList" :key="ri" :label="rItem.value">{{rItem.label}}</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'select'){
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">     
            <!--  下拉   -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-select v-model="state.dataForm.'.$ky.'" placeholder="请选择" class="w100">
                <el-option v-for="item in state.selectList"
                           :key="item.value"
                           :label="item.label"
                           :value="item.value"></el-option>
              </el-select>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'switch'){
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">
            <!-- 开关 -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-switch
                  v-model="state.dataForm.'.$ky.'"
                  size="large"
                  active-value="1"
                  inactive-value="0"
                  active-text="启用"
                  inactive-text="禁用"
                  inline-prompt
              />
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'image'){
                                    $formItemString = '          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
            <!--  上传图片  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-upload
                  class="image-uploader"
                  :action="state.uploadImgUrl"
                  :show-file-list="true"
                  :on-success="'.$handle.'"
                  accept="image/*"
                  :headers="state.header"
                  :on-remove="'.$remove.'"
                  :file-list="state.'.$listName.'"
                  list-type="picture-card"
                  :multiple="'.($ff['inputType'] > 1?'true':'false').'"
                  :limit="'.($ff['inputType']??1).'"
              >
                <el-button size="small" type="primary">点击上传</el-button>
              </el-upload>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'video'){
                                    $formItemString = '          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
            <!--  上传视频  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <video :src="state.dataForm.'.$ky.'" controls class="video" >
                <source :src="state.dataForm.'.$ky.'" />
                不支持
              </video>
              <el-upload
                  class="video-uploader"
                  :action="state.uploadVideoUrl"
                  :show-file-list="true"
                  :on-success="'.$handle.'"
                  :on-remove="'.$remove.'"
                  :file-list="state.'.$listName.'"
                  accept="video/*"
                  :headers="state.header"
                  :limit="1"
                  style="padding-left: 10px;width:50%;"
              >
                <el-button type="primary" size="default">上传视频</el-button>
              </el-upload>
              <el-input v-model="state.dataForm.'.$ky.'" placeholder="手动填写视频地址" clearable style="margin-top: 5px;"></el-input>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'file'){
                                    $formItemString = '          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
            <!--  上传文件  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-upload
                  class="file-uploader"
                  :action="state.uploadFileUrl"
                  :show-file-list="true"
                  :on-success="'.$handle.'"
                  :on-remove="'.$remove.'"
                  :file-list="state.'.$listName.'"
                  accept="*/*"
                  :headers="state.header"
                  :limit="'.($ff['inputType']??1).'"
              >
                <el-button type="primary" size="default">上传文件</el-button>
              </el-upload>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'editor'){
                                    $formItemString = '          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
          <!--  富文本编辑  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <vue-ueditor-wrap v-model="state.dataForm.'.$ky.'"
                                editor-id="editor"
                                :config="state.editorConfig"
                                :editorDependencies="['."'ueditor.config.js','ueditor.all.js'".']"
                                style="height:500px;"/>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'cascader'){
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">
          <!--  级联选择  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-cascader
                  :options="state.cascaderList"
                  :props="{ checkStrictly: true,emitPath:false, value: \'value\', label: \'label\' }"
                  placeholder="请选择'.$label.'"
                  clearable
                  class="w100"
                  v-model="state.dataForm'.$ky.'"
              >
                <template #default="{ node, data }">
                  <span>{{ data.label }}</span>
                  <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
                </template>
              </el-cascader>
            </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'checkbox'){
                                    $formItemString = '          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
          <!--  多选框  -->
              <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
                <el-checkbox v-for="(checkItem, chi) in state.checkboxList" v-model="checkItem.value" :key="chi" :label="checkItem.label" />
              </el-form-item>
          </el-col>';
                                }
                                elseif($editType == 'date'){
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">
          <!--  日期选择  -->
              <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
                  <el-date-picker
                      v-model="state.dataForm.'.$ky.'"
                      type="'.$ff['inputType'].'"
                      value-format="YYYY-MM-DD"
                      placeholder="选择'.$label.'"
                  />
              </el-form-item>
          </el-col>';
                                }
                                else{
                                    $formItemString = '          <el-col :xs="24" :sm="'.$editSize.'" :md="'.$editSize.'" :lg="'.$editSize.'" :xl="'.$editSize.'" class="mb20">
            <!--  文本  -->
            <el-form-item label="'.$label.'" prop="'.$ky.'" label-width="auto">
              <el-input v-model="state.dataForm.'.$ky.'" placeholder="请输入'.$label.'" clearable type="'.$ff['inputType'].'"></el-input>
            </el-form-item>
          </el-col>';
                                }

                                $formDataString .= $formItemString.PHP_EOL;
                            }

                            //处理编辑字段
                            $comment = $ff['comment'] ?? '';
                            $defaultValue = $dT === 'string' ? '""' : ($dT === 'array' ? '[]' : ($dT === 'bool' ? 'false' : '0'));
                            $editString .= "      {$ky}: $defaultValue,//{$comment}".PHP_EOL;
                        }


                        $controllerNameSpace = $this->createClass($controllerName,0,[
                            'modelName'=>$modelName,
                            'modelNameSpace'=>$modelNameSpace,
                            'title'=>$param['title'],
                            'search'=>$searchString,
                            'rule' => $ruleString,
                            'message' => $messageString
                        ],'admin');
                        if($controllerNameSpace){
                            //添加按钮权限
                            $nt = getDateTime('U');
                            $data = [
                                ['parent_id'=>$id,'type'=>2,'name'=>$param['name'].'Index','title'=>'列表','sort'=>1,'is_hide'=>1,'controller'=>$controllerName,'model'=>$modelName,'power'=>'index','create_time'=>$nt,'update_time'=>$nt],
                                ['parent_id'=>$id,'type'=>2,'name'=>$param['name'].'Save','title'=>'编辑','sort'=>2,'is_hide'=>1,'controller'=>$controllerName,'model'=>$modelName,'power'=>'save','create_time'=>$nt,'update_time'=>$nt],
                                ['parent_id'=>$id,'type'=>2,'name'=>$param['name'].'Delete','title'=>'删除','sort'=>3,'is_hide'=>1,'controller'=>$controllerName,'model'=>$modelName,'power'=>'delete','create_time'=>$nt,'update_time'=>$nt],
                                ['parent_id'=>$id,'type'=>2,'name'=>$param['name'].'Change','title'=>'修改','sort'=>4,'is_hide'=>1,'controller'=>$controllerName,'model'=>$modelName,'power'=>'change','create_time'=>$nt,'update_time'=>$nt]
                            ];
                            $model->saveAll($data);
                            $this->setRoute('admin',"Route::group('{$controllerName}',function(){
    Route::rule(':action','admin.{$controllerName}/:action');
});//{$param['title']}".PHP_EOL);
                        }
                        /*****************生成前端源码 *** 生成的文件只是基础架构具体逻辑需要手动修改 **************/
                        $rootPath = app()->getRootPath();
                        //生成api
                        $apiDir = $rootPath.'vueSource/src/api/'.ltrim(strtolower($param['path']),'/');
                        is_dir($apiDir) or mkdir($apiDir,0755,true);
                        if(!is_file($apiDir.'/index.ts')){
                            $apiContent = file_get_contents($this->getStub(4));
                            $apiContent = str_replace('{%model%}',$modelName,$apiContent);
                            $apiContent = str_replace('{%controller%}',$controllerName,$apiContent);
                            file_put_contents($apiDir.'/index.ts',$apiContent);//写文件
                        }

                        //生成views
                        $viewDir = $rootPath.'vueSource/src/views'.$param['path'];
                        is_dir($viewDir) or mkdir($viewDir,0755,true);
                        //1.index文件
                        if(!is_file($viewDir.'/index.vue')){
                            $indexContent = file_get_contents($this->getStub(5));
                            $indexContent = str_replace('{%model%}',$modelName,$indexContent);
                            $indexContent = str_replace('{%controller%}',$controllerName,$indexContent);
                            $indexContent = str_replace('{%path%}',$param['path'],$indexContent);
                            $indexContent = str_replace('{%tableHeader%}',$showString,$indexContent);
                            file_put_contents($viewDir.'/index.vue',$indexContent);//写文件
                        }

                        //2.search文件
                        if(!is_file($viewDir.'/search.vue')){
                            $searchContent = file_get_contents($this->getStub(6));
                            file_put_contents($viewDir.'/search.vue',$searchContent);//写文件
                        }

                        //3.dialog文件
                        if(!is_file($viewDir.'/dialog.vue')){
                            $dialogContent = file_get_contents($this->getStub(7));
                            $dialogContent = str_replace('{%model%}',$modelName,$dialogContent);
                            $dialogContent = str_replace('{%controller%}',$controllerName,$dialogContent);
                            $dialogContent = str_replace('{%path%}',$param['path'],$dialogContent);
                            $dialogContent = str_replace('{%editFormData%}',$editString,$dialogContent);
                            $dialogContent = str_replace('{%uploadListInit%}',$uploadString,$dialogContent);
                            $dialogContent = str_replace('{%uploadListString%}',$uploadListString,$dialogContent);
                            $dialogContent = str_replace('{%uploadMethod%}',$uploadMethodString,$dialogContent);
                            $dialogContent = str_replace('{%formItemString%}',$formDataString,$dialogContent);
                            $dialogContent = str_replace('{%initFileListString%}',$initFileListString,$dialogContent);
                            file_put_contents($viewDir.'/dialog.vue',$dialogContent);//写文件
                        }
                    }
                }

            }else{
                //修改
                $info = $model->find($id);
                if(!$info){
                    throw new \Exception('无效参数');
                }

                $info->allowField($fields)->save($param);
            }


        }catch (\Exception $e){
            $this->result['code'] = 500;
            $this->result['msg'] = $e->getMessage();
        }
        return returnAdmin($this->request,$this->result,$this->menuName.'-编辑',$this->adminInfo);
    }


    /**
     * 删除指定资源
     * @throws
     * @return Json
     */
    public function delete():Json
    {

        $id = input('id/d',0);

        try{
            $menu = Menu::find($id);
            if(!$menu){
                throw new \Exception('无效参数');
            }

            $count =  Db::name('menu')->where('parent_id',$id)->where('type',1)->count();
            if($count){
                throw new \Exception('请先删除所有子菜单');
            }
            $menu->delete();

            //删除下级按钮菜单
            Db::name('menu')->where('parent_id',$id)->where('type',2)->delete();


            $this->result['msg'] = '删除ID：'.$id;
        }catch (\Exception $e){
            $this->result['code'] = 500;
            $this->result['msg'] = $e->getMessage();
        }

        return returnAdmin($this->request,$this->result,$this->menuName.'-删除',$this->adminInfo);
    }

    /**
     * 保存编辑的资源
     * @throws
     * @return Json
     */
    public function addApi():Json
    {
        //获取form表单
        $param = input('param.',[]);

        $param = toUnderline($param);

        $token = 'abc123456';//验证token,请自行修改

        $rule = [
            'controller' => 'require|max:64',
            'model' => 'require|max:64',
            'token' => 'require|max:64|eq:'.$token
        ];
        $message = [
            'controller.require' => '控制器名称必填',
            'controller.max' => '控制器名称最多64个字符',
            'model.require' => '模型名称必填',
            'model.max' => '模型名称最多64个字符',
            'token.require' => 'token必填',
            'token.max' => 'token最多64个字符',
            'token.eq' => 'token错误'
        ];
        $validate = Validate::rule($rule)->message($message);
        try{
            if(!$validate->check($param)){
                throw new \Exception($validate->getError());
            }

            //生成模型和控制器
            $modelName = $param['model']??'';
            $modelName = $modelName ? ucfirst($modelName):'';
            if($modelName){
                $modelNameSpace = $this->createClass($modelName,1);
                if($modelNameSpace){
                    $controllerName = $param['controller']??'';
                    $controllerName = $controllerName ? ucfirst($controllerName):'';
                    $this->createClass($controllerName,0,[
                        'modelName'=>$modelName,
                        'modelNameSpace'=>$modelNameSpace,
                        'api'=>true
                    ],'api');
                    $this->setRoute('api',"Route::group('{$controllerName}',function(){
    Route::rule(':action','api.{$controllerName}/:action');
});".PHP_EOL);
                }
            }


        }catch (\Exception $e){
            $this->result['code'] = 500;
            $this->result['msg'] = $e->getMessage();
        }
        return jJson($this->result);
    }


    /**
     * 获取角色权限菜单id
     * @param string|int $rid
     * @return array
     * */
    protected function getPower($rid):array
    {
        $roles = Role::whereIn('id',$rid)->column('power');
        $power_ids = [];
        foreach ($roles as $power){
            $power_ids = array_merge(explode(',',$power),$power_ids);
        }
        $power_ids = array_unique($power_ids);
        $temp_ids = [];
        foreach ($power_ids as $mid){
            $temp_ids = array_merge($this->setPower($mid),$temp_ids);
        }
        return array_unique(array_merge($power_ids,$temp_ids));
    }

    /**
     * 处理权限菜单id
     * @param string|int $mid
     * @throws
     * @return array
     * */
    private function setPower($mid):array
    {
        $data = [$mid];
        $menu = Menu::find($mid);
        if(!$menu || $menu['type'] !== 1){
            return $data;
        }
        if(!$menu['parent_id'] || in_array($menu['parent_id'],$data)){
            return $data;
        }
        $pdata = $this->setPower($menu['parent_id']);

        return array_merge($data,$pdata);
    }

    /**
     * 菜单数据处理
     * @param array $data
     * @return array
     * */
    private function setMenu(array $data):array{
        $result = [];
        if(count($data)){
            foreach ($data as $d){
                $result[] = [
                    'path' => $d['path'],
                    'name' => $d['name'],
                    'component' => $d['component'],
                    'redirect' => $d['redirect'],
                    'meta' => [
                        'title' => $d['title'],
                        'isLink' => $d['link'],
                        'isHide' =>(bool) $d['is_hide'],
                        'isKeepAlive' =>(bool) $d['is_keep_alive'],
                        'isAffix' =>(bool) $d['is_affix'],
                        'isIframe' =>(bool) $d['is_iframe'],
                        'icon' => $d['icon'],
                        'roles' => []
                    ],
                    'children' => $this->setMenu($d['children'])
                ];
            }
        }
        return $result;
    }


    /**
     * 生成类
     * @param string $name
     * @param int $t
     * @param array $param
     *
     * @return bool|string
     * */
    protected function createClass(string $name,int $t,array $param=[],string $groupName=''):?string
    {
        $types = ['controller','model','middleware','validate'];
        $type = $types[$t]??'controller';

        $classname = $this->getClassName($name,$type,$groupName);

        $pathname = $this->getPathName($classname);

        if (is_file($pathname)) {
            return $classname;
        }

        if (!is_dir(dirname($pathname))) {
            mkdir(dirname($pathname), 0755, true);
        }

        if($type === 'controller'){
            if(!count($param) || !isset($param['modelName']) || !isset($param['modelNameSpace'])){
                return null;
            }
            $api = $param['api'] ?? false;
            if($api){
                $content = $this->buildApiController($classname,$param['modelName'],$param['modelNameSpace']);
            }else{
                if(!isset($param['title'])){
                    return null;
                }
                $content = $this->buildAdminController($classname,$param['modelName'],$param['modelNameSpace'],$param['title']??'',$param['search']??'',$param['rule']??'',$param['message']??'');
            }
        }else{
            $content = $this->buildOther($classname,$t);
        }

        file_put_contents($pathname, $content);
        return $classname;
    }

    /**
     * 生成控制器
     * @param string $name
     * @param string $modelName
     * @param string $modelNameSpace
     * @param string $title
     * @return string
     * */
    private function buildAdminController(string $name,string $modelName,string $modelNameSpace,string $title,string $search,string $rule,string $message):string{
        $stub = file_get_contents($this->getStub());

        $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');

        $class = str_replace($namespace . '\\', '', $name);

        return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%modelName%}','{%modelNaeSpace%}','{%title%}','{%search%}','{%rule%}','{%message%}'], [
            $class,
            $this->app->config->get('route.action_suffix'),
            $namespace,
            $modelName,
            $modelNameSpace,
            $title,
            $search,
            $rule,
            $message
        ], $stub);
    }

    /**
     * 生成控制器
     * @param string $name
     * @param string $modelName
     * @param string $modelNameSpace
     * @param string $title
     * @return string
     * */
    private function buildApiController(string $name,string $modelName,string $modelNameSpace):string{
        $stub = file_get_contents($this->getStub(8));

        $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');

        $class = str_replace($namespace . '\\', '', $name);

        return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%modelName%}','{%modelNaeSpace%}'], [
            $class,
            $this->app->config->get('route.action_suffix'),
            $namespace,
            $modelName,
            $modelNameSpace,
        ], $stub);
    }

    protected function buildOther(string $name,int $type)
    {
        $stub = file_get_contents($this->getStub($type));

        $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');

        $class = str_replace($namespace . '\\', '', $name);

        return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
            $class,
            $this->app->config->get('route.action_suffix'),
            $namespace,
            $this->app->getNamespace(),
        ], $stub);
    }

    /**
     * 获取模版文件
     * @Param int $type
     * @return string
     * */
    private function getStub(int $type = 0): string
    {
        $types = [
            'controller.stub','model.stub','middleware.stub','validate.stub',
            'vue_api.stub','vue_index.stub','vue_search.stub','vue_dialog.stub',
            'api.stub'
        ];
        $stubPath = $this->app->getAppPath() . 'stubs' . DIRECTORY_SEPARATOR;

        return $stubPath . ($types[$type] ?? 'controller.stub');

    }

    private function getPathName(string $name): string
    {
        $name = substr($name, 4);

        return $this->app->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
    }

    private function getClassName(string $name,string $type,string $groupName=''): string
    {
        if (strpos($name, '\\') !== false) {
            return $name;
        }

        if (strpos($name, '@')) {
            [$app, $name] = explode('@', $name);
        } else {
            $app = '';
        }

        if (strpos($name, '/') !== false) {
            $name = str_replace('/', '\\', $name);
        }

        return $this->getNamespace($app,$type,$groupName) . '\\' . $name;
    }

    private function getNamespace(string $app,string $type,string $groupName): string
    {
        return 'app' ."\\$type".($groupName?"\\$groupName":''). ($app ? '\\' . $app : '');
    }

    private function setRoute(string $fileName,string $content){
        $fileLike = $this->app->getRootPath() . 'route/'.$fileName.'.php';
        if(is_file($fileLike)){
            file_put_contents($fileLike, $content.PHP_EOL,8);
        }else{
            $fileHeader = '/<?php '.PHP_EOL.PHP_EOL.' use think\facade\Route;'.PHP_EOL.PHP_EOL;
            file_put_contents($fileLike, str_replace('/', '', $fileHeader).$content.PHP_EOL);
        }

    }
}
